#include "ActiveVars.hpp"

/**
 * @brief 判断传入的var是否为常量
 * 
 * @param var 传入的Value类型的变量
 * @return true var是常量
 * @return false var是变量
 */
bool isConst(Value* var){
    return var->get_name() == "";
}


void ActiveVars::run()
{
    std::ofstream output_active_vars;
    output_active_vars.open("active_vars.json", std::ios::out);
    output_active_vars << "[";
    for (auto &func : this->m_->get_functions()) {
        if (func->get_basic_blocks().empty()) {
            continue;
        }
        else
        {
            func_ = func;  

            func_->set_instr_name();
            live_in.clear();
            live_out.clear();
            
            // 在此分析 func_ 的每个bb块的活跃变量，并存储在 live_in live_out 结构内
            std::map<BasicBlock *, std::set<Value *>> def, use, phi_out, phi_use;
            //bb中有定义也有引用，但定义在引用后，也可以被丢进use。
            //同理，引用在定义后，也可以被丢进def
            //所以，只需要知道定义（引用）之前，变量有没有被引用（定义）
            //由于程序是从前往后处理的，所以后面的定义或引用自动被忽略，所以每次加入use/def之前判断是否在def/use中即可，不需要考虑之后的变化
            for(auto bb: func_->get_basic_blocks()){
                for (auto instruction: bb->get_instructions())
                {
                    if(instruction->is_phi()){
                        //phi的操作数结构为bb, var, bb, var, ...所以只能取偶数项
                        for (size_t i = 0; i < instruction->get_num_operand(); i+=2)
                        {
                            auto var = instruction->get_operand(i);
                            if(!isConst(var) && def[bb].count(var) == 0){
                                use[bb].insert(var);
                            }
                            phi_out[dynamic_cast<BasicBlock *>(instruction->get_operand(i + 1))].insert(var);
                            phi_use[bb].insert(var);
                        }
                        if(use[bb].count(instruction) == 0){
                            def[bb].insert(instruction);
                        }
                    }

                    else if (instruction->is_store())
                    {
                        //store指令不会作为操作数被引用
                        auto val = instruction->get_operand(0);  //变量
                        auto addr = instruction->get_operand(1); //地址
                        if (def[bb].count(val) == 0 && !isConst(val))
                        {
                            use[bb].insert(val);
                        }
                        if (def[bb].count(addr) == 0 && !isConst(val))
                        {
                            use[bb].insert(addr);
                        }
                    }
                    
                    else if (instruction->is_alloca())
                    {
                        if (use[bb].count(instruction) == 0)
                        {
                            def[bb].insert(instruction);
                        }
                    }
                    
                    else if (instruction->is_ret() && !instruction->is_void())
                    {
                        auto ret_instruction = dynamic_cast<ReturnInst*> (instruction);
                        auto ret = instruction->get_operand(0);
                        if (def[bb].count(ret) == 0 && !isConst(ret))
                        {
                            use[bb].insert(ret);
                        }
                    }
                    
                    else if (instruction->is_br())
                    {
                        auto br_instruction = dynamic_cast<BranchInst*> (instruction);
                        if (br_instruction->is_cond_br())
                        {
                            auto cond = br_instruction->get_operand(0);
                            if (def[bb].count(cond) == 0 && !isConst(cond))
                            {
                                use[bb].insert(cond);
                            }
                        }
                    }
                    
                    else if (instruction->is_call())
                    {
                        auto call_instruction = dynamic_cast<CallInst*> (instruction);
                        //第一个参数lable不是活跃变量，后面的为函数的参数
                        for (size_t i = 1; i < call_instruction->get_num_operand(); i++)
                        {
                            auto arg = call_instruction->get_operand(i);
                            if (def[bb].count(arg) == 0 && !isConst(arg))
                            {
                                use[bb].insert(arg);
                            }
                        }
                        if(!instruction->is_void()){
                            if (use[bb].count(instruction) == 0)
                            {
                                def[bb].insert(instruction);
                            }
                        }
                    }
                    
                    else if (instruction->is_gep())
                    {
                        auto gep_instruction = dynamic_cast<GetElementPtrInst*> (instruction);
                        for (auto val: gep_instruction->get_operands()){
                            if (def[bb].count(val) == 0 && !isConst(val))
                            {
                                use[bb].insert(val);
                            }
                        }
                        if (use[bb].count(instruction) == 0)
                        {
                            def[bb].insert(instruction);
                        }
                        
                    }
                    
                    else{
                        for (auto val: instruction->get_operands()){
                            if (def[bb].count(val) == 0 && !isConst(val))
                            {
                                use[bb].insert(val);
                            }
                        }
                        if (use[bb].count(instruction) == 0)
                        {
                            def[bb].insert(instruction);
                        }
                    }
                }
            }

            //定义一个标识，用来本次迭代有没有改变in和out
            //如果没有改变则结束循环
            bool is_changed = true;
            
            while (is_changed)
            {
                is_changed = 0;
                for (auto bb : func_->get_basic_blocks())
                {
                    //对out插入后继基本块的in，但是要剔除后继块的phi指令的处理结果。
                    //遇到后继块中的phi指令使用的变量，但是不是后继块phi指令中本块对应的变量，要剔除
                    live_out[bb].clear();
                    for (auto bb_succ : bb->get_succ_basic_blocks())
                    {
                        live_out[bb].insert(live_in[bb_succ].begin(), live_in[bb_succ].end());
                        for (auto var : live_in[bb_succ])
                        {
                            if (phi_use[bb_succ].count(var) != 0 && phi_out[bb].count(var) == 0)
                            {
                                live_out[bb].erase(var);
                            }
                        }
                    }

                    //out-def+use
                    std::set<Value *> live_in_var_last = live_in[bb];   //上一次的in，用于判断是否改变
                    live_in[bb] = live_out[bb];
                    for (auto &defItem : def[bb])
                    {
                        if (live_in[bb].count(defItem) != 0)
                        {
                            live_in[bb].erase(defItem);
                        }
                    }
                    live_in[bb].insert(use[bb].begin(), use[bb].end());
                    if (live_in_var_last.size() != live_in[bb].size())
                    {
                        is_changed = true;
                    }
                }
            }
            
            output_active_vars << print();
            output_active_vars << ",";
        }
    }
    output_active_vars << "]";
    output_active_vars.close();
    return ;
}

std::string ActiveVars::print()
{
    std::string active_vars;
    active_vars +=  "{\n";
    active_vars +=  "\"function\": \"";
    active_vars +=  func_->get_name();
    active_vars +=  "\",\n";

    active_vars +=  "\"live_in\": {\n";
    for (auto &p : live_in) {
        if (p.second.size() == 0) {
            continue;
        } else {
            active_vars +=  "  \"";
            active_vars +=  p.first->get_name();
            active_vars +=  "\": [" ;
            for (auto &v : p.second) {
                active_vars +=  "\"%";
                active_vars +=  v->get_name();
                active_vars +=  "\",";
            }
            active_vars += "]" ;
            active_vars += ",\n";   
        }
    }
    active_vars += "\n";
    active_vars +=  "    },\n";
    
    active_vars +=  "\"live_out\": {\n";
    for (auto &p : live_out) {
        if (p.second.size() == 0) {
            continue;
        } else {
            active_vars +=  "  \"";
            active_vars +=  p.first->get_name();
            active_vars +=  "\": [" ;
            for (auto &v : p.second) {
                active_vars +=  "\"%";
                active_vars +=  v->get_name();
                active_vars +=  "\",";
            }
            active_vars += "]";
            active_vars += ",\n";
        }
    }
    active_vars += "\n";
    active_vars += "    }\n";

    active_vars += "}\n";
    active_vars += "\n";
    return active_vars;
}